package com.cgt.xiaotao.user.service.impl;

import com.cgt.common.exception.BizIllegalException;
import com.cgt.common.utils.BooleanUtils;
import com.cgt.common.utils.CollUtils;
import com.cgt.common.utils.DateUtils;
import com.cgt.common.utils.UserContext;
import com.cgt.xiaotao.user.contants.RedisConstants;
import com.cgt.xiaotao.user.domain.po.PointsRecord;
import com.cgt.xiaotao.user.domain.vo.SignResultVO;
import com.cgt.xiaotao.user.mapper.PointsRecordMapper;
import com.cgt.xiaotao.user.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;

@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {


    private final StringRedisTemplate redisTemplate;
    private final PointsRecordMapper pointsRecordMapper;

    @Override
    public SignResultVO addSignRecords() {
        // 1.签到
        // 1.1.获取登录用户
        Long userId = UserContext.getUser();
        // 1.2.获取日期
        LocalDate now = LocalDate.now();
        // 1.3.拼接key
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX
                + userId
                + now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        // 1.4.计算offset
        int offset = now.getDayOfMonth() - 1;
        // 1.5.保存签到信息
        Boolean exists = redisTemplate.opsForValue().setBit(key, offset, true);
        if (BooleanUtils.isTrue(exists)) {
            throw new BizIllegalException("不允许重复签到！");
        }
        // 2.计算连续签到天数
        int signDays = countSignDays(key, now.getDayOfMonth());
        // 3.计算签到得分
        int rewardPoints = 0;
        switch (signDays) {
            case 7:
                rewardPoints = 10;
                break;
            case 14:
                rewardPoints = 20;
                break;
            case 28:
                rewardPoints = 40;
                break;
        }
        // TODO 4.保存积分明细记录
        // 先查询points_record表中是否有用户积分记录
        PointsRecord pointsRecord = pointsRecordMapper.selectById(userId);

        // 如果有则更新
        if (pointsRecord != null) {
            pointsRecord.setPoints(pointsRecord.getPoints() + 1 + rewardPoints);
            pointsRecordMapper.updateById(pointsRecord);
        }
        // 如果没有则新增
        else {
            pointsRecord = new PointsRecord();
            pointsRecord.setId(userId);
            pointsRecord.setUserId(userId);
            pointsRecord.setPoints(1 + rewardPoints);
            pointsRecordMapper.insert(pointsRecord);
        }

        // 5.封装返回
        SignResultVO vo = new SignResultVO();
        vo.setSignDays(signDays);
        vo.setRewardPoints(rewardPoints);
        return vo;
    }

    @Override
    public Integer repairSign(String date) {
        // 1. 获取登录用户
        Long userId = UserContext.getUser();

        // 2. 解析日期 (支持 yyyy-MM-dd 格式)
        LocalDate repairDate = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));

        // 3. 拼接key
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX
                + userId
                + repairDate.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);

        // 4. 计算offset
        int offset = repairDate.getDayOfMonth() - 1;

        // 5. 保存签到信息（补签）
        Boolean exists = redisTemplate.opsForValue().setBit(key, offset, true);
        // 注意：补签不检查是否已签到
        if (BooleanUtils.isTrue(exists)) {
            throw new BizIllegalException("不允许重复签到！");
        }

        // 6. 更新积分（只给基础积分1分，无连续签到奖励）
        PointsRecord pointsRecord = pointsRecordMapper.selectById(userId);

        // 如果有则更新
        if (pointsRecord != null) {
            pointsRecord.setPoints(pointsRecord.getPoints() + 1);
            pointsRecord.setUpdateTime(new Date());
            pointsRecordMapper.updateById(pointsRecord);
        }
        // 如果没有则新增
        else {
            pointsRecord = new PointsRecord();
            pointsRecord.setId(userId);
            pointsRecord.setUserId(userId);
            pointsRecord.setPoints(1);
            pointsRecord.setCreateTime(new Date());
            pointsRecord.setUpdateTime(new Date());
            pointsRecordMapper.insert(pointsRecord);
        }

        // 7. 返回签到得分1
        return 1;
    }

    @Override
    public Integer queryTotalPoints() {
        Long userId = UserContext.getUser();
        PointsRecord pointsRecord = pointsRecordMapper.selectById(userId);
        if (pointsRecord != null) {
            return pointsRecord.getPoints();
        }
        return 0;
    }

    private int countSignDays(String key, int len) {
        // 1.获取本月从第一天开始，到今天为止的所有签到记录
        List<Long> result = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(
                        BitFieldSubCommands.BitFieldType.unsigned(len)).valueAt(0));
        if (CollUtils.isEmpty(result)) {
            return 0;
        }
        int num = result.get(0).intValue();
        // 2.定义一个计数器
        int count = 0;
        // 3.循环，与1做与运算，得到最后一个bit，判断是否为0，为0则终止，为1则继续
        while ((num & 1) == 1) {
            // 4.计数器+1
            count++;
            // 5.把数字右移一位，最后一位被舍弃，倒数第二位成了最后一位
            num >>>= 1;
        }
        return count;
    }

    @Override
    public Byte[] querySignRecords() {
        //1.获取用户id
        Long userId = UserContext.getUser();
        //2.拼接key
        LocalDate now = LocalDate.now();
        String format = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));//得到对应格式
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId.toString() + format;
        //3.利用bitfield 命令获取本月第一天到今天的签到记录
        int dayOfMonth = now.getDayOfMonth();
        List<Long> bitField = redisTemplate.opsForValue().bitField(
                key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
        );
        if (CollUtils.isEmpty(bitField)) {
            return new Byte[0];
        }
        Long num = bitField.get(0);
        int offset = dayOfMonth - 1;
        //4.利用与运算和位移运算，封装结果
        Byte[] arr = new Byte[dayOfMonth];
        while (offset >= 0) {
            arr[offset] = (byte) (num & 1);
            offset--;
            num = num >>> 1;
        }
        return arr;
    }

    @Override
    public Byte[] querySignRecords(Integer year, Integer month) {
        //1.获取用户id
        Long userId = UserContext.getUser();

        //2.构造年月日期
        LocalDate date = LocalDate.of(year, month, 1);

        //3.拼接key
        String format = date.format(DateTimeFormatter.ofPattern(":yyyyMM"));//得到对应格式
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId.toString() + format;

        //4.获取该月总天数
        int daysInMonth = date.lengthOfMonth();

        //5.利用bitfield 命令获取本月第一天到最后一天的签到记录
        List<Long> bitField = redisTemplate.opsForValue().bitField(
                key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(daysInMonth)).valueAt(0)
        );
        if (CollUtils.isEmpty(bitField)) {
            return new Byte[0];
        }
        Long num = bitField.get(0);
        int offset = daysInMonth - 1;

        //6.利用与运算和位移运算，封装结果
        Byte[] arr = new Byte[daysInMonth];
        while (offset >= 0) {
            arr[offset] = (byte) (num & 1);
            offset--;
            num = num >>> 1;
        }
        return arr;
    }
}
